import React, { useCallback, useEffect, useRef } from "react"
import { Image, Input, ScrollView, Textarea, View } from "@tarojs/components"
import GpContainer from "@/components/GpContainer"
import Taro, { useRouter } from "@tarojs/taro"
import { useSetState } from "@/utils/hooks"
import styles from "./index.module.scss"
import Icon from "@/components/GpFontIcon"
import {
  publishNote,
  quickSearchTopic,
  uploadV4,
  quickSearchTags,
  GetNoteDetail,
  modifyMyNote
} from "@/apis/yikao-community"
import { requestIsFullfilled } from "@/utils/request"
import clsx from "clsx"
import { TextareaProps } from "@tarojs/components/types/Textarea"
import { Popup } from "@taroify/core"
import { ContentNoteAttachmentType } from "@/types/enum"
import { pLimit } from "@vinsurs/p-limit"
import { throttle } from "@/utils/common"

type IAttachment = {
  fileId: number
  fileUrl: string
}
type IState = {
  attachments: IAttachment[]
  title: string
  content: string
  /** 标签输入内容 */
  tagContent: string
  tagContentFocus: boolean
  /** 推荐标签 */
  suggestedTags: IOptionLike<number>[]
  currentTopic: IOptionLike<number> | null
  /** 热门话题 */
  hotTopics: IOptionLike<number>[]
  openHotTopic: boolean
}
type IParams = {
  /** 话题id */
  topicId?: string
  /** 笔记id
   * @only 编辑时
   */
  id?: string
}
/**
 * @page 发布笔记
 */
export default function NotePublish() {
  const config = {
    maxVideoCount: 1,
    maxPictureCount: 18,
    maxVideoSize: 100 * 1024 * 1024,
    flagKey: "#",
    flagKeyReg: /#/,
    spaceKeyReg: /\s/
  }
  const router = useRouter<IParams>()
  const [state, setState] = useSetState<IState>({
    attachments: [],
    title: "",
    content: "",
    tagContent: "",
    tagContentFocus: false,
    suggestedTags: [],
    currentTopic: null,
    hotTopics: [],
    openHotTopic: false
  })
  const fileTypeRef = useRef<ContentNoteAttachmentType>()
  // eslint-disable-next-line react-hooks/exhaustive-deps
  const searchTags = useCallback(
    throttle((keywords?: string) => {
      quickSearchTags({
        keywords
      }).then(res => {
        if (requestIsFullfilled(res)) {
          const tags = res.data.data
          setState({
            suggestedTags: tags
          })
        }
      })
    }) as any,
    []
  )
  const triggerTagSearch = useRef<boolean>(false)
  const tagContentCursorRef = useRef<number>(-1)
  const topicIdRef = useRef<number>()
  useEffect(() => {
    if (router.params.topicId) {
      topicIdRef.current = Number(router.params.topicId)
    }
    if (router.params.id) {
      Taro.showLoading({
        title: "加载中..."
      })
      GetNoteDetail({ id: Number(router.params.id) })
        .then(res => {
          Taro.hideLoading()
          if (requestIsFullfilled(res)) {
            const detail = res.data.data
            fileTypeRef.current = detail.fileType
            const patch: Partial<IState> = {}
            if (detail.files && detail.files.length > 0) {
              patch.attachments = detail.files.map(l => {
                return {
                  fileId: l.id,
                  fileUrl: l.fileImage
                }
              })
            }
            if (detail.title) {
              patch.title = detail.title
            }
            if (detail.content) {
              patch.content = detail.content
            }
            if (detail.tags && detail.tags.length > 0) {
              patch.tagContent = detail.tags.map(l => config.flagKey.concat(l)).join("")
            }
            setState(patch)
          }
        })
        .catch(() => {
          Taro.hideLoading()
        })
    } else {
      initialUpload()
    }
    fetchHotTopics()
  }, [])
  function fetchHotTopics() {
    const defaultValues: number[] = []
    if (topicIdRef.current) {
      defaultValues.push(topicIdRef.current)
    }
    quickSearchTopic({
      defaultValues,
      top: 20
    }).then(res => {
      if (requestIsFullfilled(res)) {
        const hotTopics = res.data.data
        let currentTopic = state.currentTopic
        if (topicIdRef.current) {
          const finder = hotTopics.find(l => l.value === topicIdRef.current)
          if (finder) {
            currentTopic = finder
          }
        }
        setState({
          currentTopic,
          hotTopics
        })
      }
    })
  }
  function handlePublish() {
    const params: IApiRelative.IPublishNote.IParams = {
      content: ""
    }
    if (state.attachments.length === 0) {
      Taro.showToast({
        title: "请上传附件",
        icon: "none"
      })
      return
    }
    params.fileType = fileTypeRef.current
    params.fileIds = state.attachments.map(l => l.fileId)
    if (state.title) {
      params.title = state.title
    }
    if (!state.content) {
      Taro.showToast({
        title: "请输入评论内容",
        icon: "none"
      })
      return
    }
    params.content = state.content
    if (state.tagContent && state.tagContent.trim().length > 0) {
      const tags: string[] = []
      const tagContent = state.tagContent.trim()
      const firstIsTag = tagContent.startsWith(config.flagKey)
      const frgs = tagContent.split(config.flagKey)
      if (!firstIsTag) {
        frgs.shift()
      }
      frgs.forEach(frg => {
        const part = frg.trim()
        if (part.length > 0) {
          const [tag] = part.split(/\s+/)
          tag && tags.push(tag)
        }
      })
      params.tags = tags
    }
    if (state.currentTopic) {
      params.discussionId = state.currentTopic.value
    }
    Taro.showLoading({ title: "提交中..." })
    const isEdit = router.params.id ? true : false
    const action = isEdit
      ? modifyMyNote({ id: Number(router.params.id) }, params)
      : publishNote(params)
    action
      .then(res => {
        Taro.hideLoading()
        if (requestIsFullfilled(res)) {
          Taro.showToast({
            title: isEdit ? "修改成功" : "发布成功",
            icon: "none"
          })
          Taro.navigateBack()
        }
      })
      .catch(() => {
        Taro.hideLoading()
      })
  }
  /**
   * 处理标签内容输入事件
   * - keyCode: 35 #键
   * - keyCode: 32 空格键
   * - keyCode: 8 删除退格键
   * @note **组件事件官方bug: 使用keyCode判断有bug, 一旦输入了退格删除键, 以后不管输入什么, keyCode都返回了8**
   * @description 输入#键代表触发标签搜索, 输入空格键代表结束标签
   */
  function handleTagInput(ev: { detail: TextareaProps.onInputEventDetail }) {
    const { value, cursor } = ev.detail
    tagContentCursorRef.current = cursor
    if (cursor <= 0) {
      setState({
        suggestedTags: [],
        tagContent: value
      })
      triggerTagSearch.current = false
      return
    }
    setState({ tagContent: value })
    // 输入的是否是#键
    const isFlagKey = !!value.charAt(cursor - 1).match(config.flagKeyReg)
    // 输入的是否是空格键
    const isSpaceKey = !!value.charAt(cursor - 1).match(config.spaceKeyReg)
    if (isFlagKey) {
      triggerTagSearch.current = true
    } else if (isSpaceKey) {
      triggerTagSearch.current = false
    } else {
      if (triggerTagSearch.current) {
        const flagIndex = value.lastIndexOf(config.flagKey, cursor - 1)
        const keywords = value.slice(flagIndex + 1, cursor)
        searchTags(keywords)
      }
    }
  }
  function handleChooseTopic(topic: IOptionLike<number>) {
    setState({ currentTopic: topic, openHotTopic: false })
  }
  function initialUpload() {
    Taro.chooseMedia({
      count: 1,
      maxDuration: 60,
      success: res => {
        const [file] = res.tempFiles
        if (res.type === "image") {
          fileTypeRef.current = ContentNoteAttachmentType.Image
        } else if (res.type === "video") {
          // 检测视频文件大小是否超出限制100M
          if (file.size > config.maxVideoSize) {
            Taro.showModal({
              content: "视频文件大小超出限制(最大100M)"
            })
            return
          }
          fileTypeRef.current = ContentNoteAttachmentType.Video
        }
        // 上传文件
        uploadV4(file.tempFilePath).then(response => {
          const resp = JSON.parse(response.data)
          if (resp.success) {
            const { id, image } = resp.data
            const attachments = state.attachments.concat()
            attachments.push({
              fileId: id,
              fileUrl: image
            })
            setState({ attachments })
          }
        })
      }
    })
  }
  function handleTriggerUpload() {
    if (typeof fileTypeRef.current === "undefined") {
      initialUpload()
      return
    }
    const isImageAttachmentType = fileTypeRef.current === ContentNoteAttachmentType.Image
    const count = isImageAttachmentType
      ? config.maxPictureCount - state.attachments.length
      : config.maxVideoCount - state.attachments.length
    const mediaType: any = isImageAttachmentType ? ["image"] : ["video"]
    Taro.chooseMedia({
      count,
      mediaType,
      maxDuration: 60,
      success: res => {
        // 检测视频文件大小是否超出限制100M
        if (!isImageAttachmentType && res.tempFiles[0].size > config.maxVideoSize) {
          Taro.showModal({
            content: "视频文件大小超出限制(最大100M)"
          })
          return
        }
        const len = res.tempFiles.length
        const attachments: IAttachment[] = []
        const tasks: any = res.tempFiles.map(file => {
          return () => uploadTask(file.tempFilePath)
        })
        pLimit(tasks, { concurrency: 3 })
        function uploadTask(tempFilePath) {
          return uploadV4(tempFilePath)
            .then(response => {
              const resp = JSON.parse(response.data)
              if (resp.success) {
                const { id, image } = resp.data
                attachments.push({
                  fileId: id,
                  fileUrl: image
                })
              }
            })
            .finally(onFinally)
        }
        function onFinally() {
          if (len === attachments.length) {
            setState({
              attachments: state.attachments.concat(attachments)
            })
          }
        }
      }
    })
  }
  function handleClickAttachment(attachment: IAttachment) {
    Taro.showActionSheet({
      itemList: ["删除"],
      success: ({ tapIndex }) => {
        if (tapIndex === 0) {
          Taro.showModal({
            content: "确定删除该附件吗？",
            success: ({ confirm }) => {
              if (confirm) {
                const attachments = state.attachments.filter(l => l.fileId !== attachment.fileId)
                setState({
                  attachments
                })
              }
            }
          })
        }
      }
    })
  }
  function handleClickTagBtn() {
    setState(prevState => ({
      tagContentFocus: true,
      tagContent: prevState.tagContent.concat(config.flagKey)
    }))
    triggerTagSearch.current = true
  }
  function handleChooseTag(tag: IOptionLike<number>) {
    const flagIndex = state.tagContent.lastIndexOf(config.flagKey, tagContentCursorRef.current - 1)
    const left = state.tagContent.slice(0, flagIndex + 1)
    const right = state.tagContent.slice(tagContentCursorRef.current)
    setState({
      tagContent: left + tag.label + right,
      tagContentFocus: false,
      suggestedTags: []
    })
    triggerTagSearch.current = false
    tagContentCursorRef.current = -1
  }
  /** 渲染上传图片区域
   *
   * 1、用户一点击先选择至少一张图片（或视频）上传。上传图片尺寸不限制，若图片宽超过1280或长超过2000则按等比例压缩；
   *
    2、上传视频限制在100MB以内；

    3、选视频时就不能选图片（只能选1个视频），选图片时最多选18张
   */
  function renderAttachments() {
    return (
      <View className={styles.uploadArea}>
        {state.attachments.map(l => (
          <View
            className={clsx(styles.box, styles.imageWrap)}
            key={l.fileId}
            onClick={() => handleClickAttachment(l)}
          >
            <Image src={l.fileUrl} className={styles.image} mode="aspectFill" />
          </View>
        ))}
        <View className={clsx(styles.box, styles.uploadTrigger)} onClick={handleTriggerUpload}>
          <Icon icon="icon-tianjia" size={48} color="#999999" />
          <View className={styles.triggerTip}>视频 / 图片</View>
        </View>
      </View>
    )
  }
  /** 渲染标签区域
   *
   * 话题和标签不是同一个，用户可以在发布内容时输入#号（或点击#标签按钮插入#）选择库里的标签，支持自定义创建标签
   */
  function renderTagArea() {
    return (
      <View className={styles.tagArea}>
        <View className={styles.tagTextarea}>
          <Textarea
            value={state.tagContent}
            onInput={handleTagInput}
            placeholder="请在此处输入标签(需要带#号，如“#艺考日常”)"
            cursorSpacing={200}
            adjustPosition
            className={styles.tagText}
            autoHeight
            maxlength={-1}
            placeholderClass={styles.tagTextPlaceholder}
            confirmType="done"
            focus={state.tagContentFocus}
            onBlur={() => setState({ tagContentFocus: false })}
            onFocus={() => setState({ tagContentFocus: true })}
            disableDefaultPadding
          />
        </View>
        <View className={styles.tagBtn} onClick={handleClickTagBtn}>
          <Icon icon="icon-huati4" color="#333333" size={24} />
          标签
        </View>
        {renderSuggestedTags()}
      </View>
    )
  }
  function renderSuggestedTags() {
    if (state.suggestedTags.length === 0) return null
    return (
      <View className={styles.suggestTagList}>
        {state.suggestedTags.map(l => (
          <View className={styles.suggestTag} key={l.value} onClick={() => handleChooseTag(l)}>
            <View className={styles.suggestTagText}>
              <Icon icon="icon-huati4" color="#333333" size={24} />
              {l.label}
            </View>
            <View className={styles.viewCount}>{l.count}次浏览</View>
          </View>
        ))}
      </View>
    )
  }
  /** 渲染热门话题popup */
  function renderHotTopics() {
    return (
      <Popup
        open={state.openHotTopic}
        rounded
        placement="bottom"
        className={styles.hotTopicPopup}
        onClose={opened => setState({ openHotTopic: opened })}
      >
        <Popup.Backdrop catchMove />
        <Popup.Close />
        <View className={styles.popupTitle}>
          <Image
            src="/images/yikao-community/topicdeep.png"
            className="w-28px h-28px object-cover mr-8px"
          />
          热门话题
        </View>
        <ScrollView scrollY className={styles.popupScrollView}>
          {state.hotTopics.map((l, index) => {
            const src = `/images/yikao-community/sort${Math.min(index + 1, 4)}.png`
            return (
              <View
                className={styles.hotTopicItemWrap}
                key={l.value}
                onClick={() => handleChooseTopic(l)}
              >
                <View className={styles.hotTopicItem}>
                  <View className={styles.hotTopicItemInner}>
                    <Image
                      src={src}
                      className="absolute left-0 right-0 top-0 bottom-0 w-full h-full block box-border"
                    />
                    <View className={styles.hotTopicHot}>{index + 1}</View>
                  </View>
                  <View className={styles.hotTopicTitle}>#{l.label}</View>
                </View>
              </View>
            )
          })}
        </ScrollView>
      </Popup>
    )
  }
  return (
    <GpContainer
      className={styles.container}
      style={{
        paddingBottom: "200px"
      }}
    >
      {renderAttachments()}
      <View className={styles.titleWrap}>
        <Input
          value={state.title}
          onInput={ev => setState({ title: ev.detail.value })}
          className={styles.titleInput}
          placeholder="填写标题（选填）"
          placeholderClass={styles.titlePlaceholder}
        />
      </View>
      <View className={styles.textContainer}>
        <Textarea
          value={state.content}
          onInput={ev => setState({ content: ev.detail.value })}
          placeholder="填写正文，和大家分享一下吧！"
          cursorSpacing={0}
          adjustPosition
          className={styles.textArea}
          autoHeight={false}
          maxlength={-1}
          placeholderClass={styles.textAreaPlaceholder}
        />
      </View>
      {renderTagArea()}
      <View className={styles.topicArea} onClick={() => setState({ openHotTopic: true })}>
        <View className="text-bold text-28px flex-none">
          <Icon icon="icon-huati3" size={28} color="#333333" className="inline-block mr-8px" />
          所属话题:
        </View>
        <View className="text-bold text-28px flex items-center justify-end flex-1 w-0 ml-16px">
          <View
            className={clsx("truncate", {
              [styles.pcolor]: !state.currentTopic
            })}
          >
            {state.currentTopic?.label || "选择话题"}
          </View>
          <Icon icon="icon-youjiantou" color="#666666" size={24} className="ml-16px" />
        </View>
      </View>
      <View className={styles.actionBtn}>
        <View
          className="w-598px h-88px box-border flex flex-row justify-center items-center rounded-44px bg-primary text-32px text-white font-500"
          onClick={handlePublish}
        >
          提交发布
        </View>
      </View>
      {renderHotTopics()}
    </GpContainer>
  )
}
